--[[
    desc: 主城模式切换组件
    author:Hoo
    time:2022-01-03 13:31:52
]]

--- @class CityModeSwitchComponent
local CityModeSwitchComponent = Class("CityModeSwitchComponent")
local M = CityModeSwitchComponent

local NORMAL_TAG  = 100
local SPECIAL_TAG = 200
local blendTimer = nil
local delayObjTimer = nil

M.NormalEnum = {
    Unlock         = 100,        -- 引导员
    Shop           = 101,        -- 商店
    TimeBox        = 102,        -- 时光魔晶
    SupSkill       = 103,        -- 女神
    Pet            = 104,        -- 飞飞
    --PetShop        = 105,        -- 飞飞交易行
    HeroManor      = 106,        -- 家园
    Gallery        = 105,        -- 画廊
}
M.SpecialEnum = {
    HighRank       = 200,        -- 传令员
    Guild          = 201,        -- 战团
    DarkStreet     = 202,        -- 暗巷
    Ranking        = 203,        -- 排行榜
    Notice         = 204,        -- 公告牌
    GuildRank      = 205         -- 争霸冠军
}

--- 主城模式
M.CityModeEnum = {
    Normal    = 0,   -- 普通模式
    Charactor = 1,   -- 立绘模式
}

local TEXT_SPEED = 0.1  -- 文字播放速度：s
local TEXT_DELAY = 3    -- 文字延迟消失时间：s

--- 计时器
local mTimer = nil
local bCanClick = true
local drawIndex = 1

--- @param root GObject 根节点
function M:ctor(root)
    self.ctrlModeSwitch     = root:GetController("Type")
    self.roleDraw = {}
    self.drawParent = {}
    self.drawWrapper  = {}
    self.drawParent[1]         = root:GetChild("Graph_HeroModel")
    self.drawParent[2]         = root:GetChild("Graph_HeroModel2")
    self.listNormalFunc     = root:GetChild("List_NormalFunc")
    self.listSpecialFunc    = root:GetChild("List_SpecialFunc")
    self.btnCharactorChange = root:GetChild("btn_ChangeCharactor")
    self.btnModeSwitch      = root:GetChild("Btn_Switch")
    self.btnBlank           = root:GetChild("Btn_ShouQi")
    self.txtHeroSpeak       = root:GetChild("HeroSpeakText")
    self.imgBgTop           = root:GetChild("home_home_Arch_lhbg01")
    self.imgBgBottom        = root:GetChild("home_home_Arch_lhbg02")
    self.btnNormalFunc      = root:GetChild("Btn_NormalFunc")
    self.btnSpecialFunc     = root:GetChild("Btn_SpecialFunc")
    self.effTextHide        = root:GetTransition("Effect_TextHide")
    self.ctrlNormalFunc     = root:GetController("NormalAni")
    self.ctrlSpecialFunc    = root:GetController("SpecialAni")
    self.ctrlSwitchBtn      = self.btnModeSwitch:GetController("Switch")

    self.listNormalFunc.onClickItem:Add(self.onClickNormalFuncItem, self)
    self.listSpecialFunc.onClickItem:Add(self.onClickSpecialFuncItem, self)
    self.btnCharactorChange.onClick:Add(self.onClickCharactorChange, self)
    self.btnBlank.onClick:Add(self.onClickBlank, self)
    self.btnModeSwitch.onClick:Add(self.onClickModeSwitch, self)
    self.btnNormalFunc.onClick:Add(self.onClickNormalFunc, self)
    self.btnSpecialFunc.onClick:Add(self.onClickSpecialFunc, self)
    ListenEvent(Config.EventType.FreashSettingDraw,    self.loadCityModeCharactor, self)
    ListenEvent(Config.EventType.FreashCityBackground, self.loadCityBackground,    self)
    -- ListenEvent(Config.EventType.FreashRoleShowDraw, self.loadCityModeCharactor, self)
    ListenEvent(Config.EventType.CityModeReset,    self.onEventResetMode,        self)
    ListenEvent(Config.EventType.BottomChangePage, self.onEventBottomChangePage, self)

    --- 小红点相关
    local funcBtnUnlock     = self.listNormalFunc:GetChildAt(0)
    local funcBtnShop       = self.listNormalFunc:GetChildAt(1)
    local funcBtnTimeBox    = self.listNormalFunc:GetChildAt(2)
    local funcBtnSupSkill   = self.listNormalFunc:GetChildAt(3)
    local funcBtnPet        = self.listNormalFunc:GetChildAt(4)
    -- local funcBtnPetShop    = self.listNormalFunc:GetChildAt(5)
    local funBtnHeroManor   = self.listNormalFunc:GetChildAt(6)
    local funcBtnGallery    = self.listNormalFunc:GetChildAt(5)
    local funcBtnHighRank   = self.listSpecialFunc:GetChildAt(0)
    local funcBtnGuild      = self.listSpecialFunc:GetChildAt(1)
    local funcBtnDarkStreet = self.listSpecialFunc:GetChildAt(2)
    local funcBtnRanking    = self.listSpecialFunc:GetChildAt(3)
    local funcBtnNotice     = self.listSpecialFunc:GetChildAt(4)
    local funcBtnGuildRank  = self.listSpecialFunc:GetChildAt(5)
    UIRedDots.addViewListen(PanelResNames.UICity, "city" .. "normalFunc",             self.btnNormalFunc,     Config.RedDotsType.CityNormalFunc)
    UIRedDots.addViewListen(PanelResNames.UICity, "city" .. "specialFunc",            self.btnSpecialFunc,    Config.RedDotsType.CitySpecialFunc)
    UIRedDots.addViewListen(PanelResNames.UICity, "city" .. M.NormalEnum.Unlock,      funcBtnUnlock,     Config.RedDotsType.UnlockDesc)
    UIRedDots.addViewListen(PanelResNames.UICity, "city" .. M.NormalEnum.TimeBox,     funcBtnTimeBox,    Config.RedDotsType.TimeBoxAll)
    UIRedDots.addViewListen(PanelResNames.UICity, "city" .. M.NormalEnum.SupSkill,    funcBtnSupSkill,   Config.RedDotsType.AssistantSkill)
    UIRedDots.addViewListen(PanelResNames.UICity, "city" .. M.NormalEnum.Pet,         funcBtnPet,        Config.RedDotsType.PetPartUp)
    UIRedDots.addViewListen(PanelResNames.UICity, "city" .. M.NormalEnum.HeroManor,   funBtnHeroManor,        Config.RedDotsType.HeroManor)
    UIRedDots.addViewListen(PanelResNames.UICity, "city" .. M.SpecialEnum.Guild,      funcBtnGuild,      Config.RedDotsType.AllGuild)
    UIRedDots.addViewListen(PanelResNames.UICity, "city" .. M.SpecialEnum.DarkStreet, funcBtnDarkStreet, Config.RedDotsType.DarkStreet)
    UIRedDots.addViewListen(PanelResNames.UICity, "city" .. M.SpecialEnum.Ranking,    funcBtnRanking,    Config.RedDotsType.UIRankingLike)
    UIRedDots.addViewListen(PanelResNames.UICity, "city" .. M.SpecialEnum.Notice,     funcBtnNotice,     Config.RedDotsType.Billboard, logicMgr.NoticeManager.PartBigType.billboard)
    
    -- 功能开启条件管理
    self.funcOpenDict = {}
    self.funcOpenDict[tostring(M.NormalEnum.Shop)]        = {Config.FuncOpenType.Shop, funcBtnShop} 
    self.funcOpenDict[tostring(M.NormalEnum.SupSkill)]    = {Config.FuncOpenType.SupSkillCultivate, funcBtnSupSkill}
    self.funcOpenDict[tostring(M.NormalEnum.Unlock)]      = {Config.FuncOpenType.UnlockFunc, funcBtnUnlock}
    self.funcOpenDict[tostring(M.NormalEnum.Pet)]         = {Config.FuncOpenType.Pet, funcBtnPet}
    -- self.funcOpenDict[tostring(M.NormalEnum.PetShop)]     = {Config.FuncOpenType.PetShop, funcBtnPetShop}
    self.funcOpenDict[tostring(M.NormalEnum.TimeBox)]     = {Config.FuncOpenType.TimeBox1, funcBtnTimeBox}
    self.funcOpenDict[tostring(M.SpecialEnum.HeroManor)]  = {Config.FuncOpenType.HeroManor, funBtnHeroManor}
    self.funcOpenDict[tostring(M.SpecialEnum.HighRank)]   = {Config.FuncOpenType.CrossTeamArena, funcBtnHighRank}
    self.funcOpenDict[tostring(M.SpecialEnum.Guild)]      = {Config.FuncOpenType.Guild, funcBtnGuild}
    self.funcOpenDict[tostring(M.SpecialEnum.Notice)]     = {Config.FuncOpenType.OperationNotice, funcBtnNotice}
    self.funcOpenDict[tostring(M.SpecialEnum.DarkStreet)] = {Config.FuncOpenType.DarkStreet, funcBtnDarkStreet}

    -- 当前模式
    local currMode = self:getCurrMode()
    self.ctrlModeSwitch.selectedIndex = currMode

    -- 主城立绘人物形象
    --self:loadCityModeCharactor()
    self:loadCityBackground()
    self:refreshHeroText("")
end

function M:destroy()
    self.listNormalFunc.onClickItem:Clear()
    self.listSpecialFunc.onClickItem:Clear()
    self.btnCharactorChange.onClick:Clear()
    self.btnBlank.onClick:Clear()
    self.btnModeSwitch.onClick:Clear()
    self.btnNormalFunc.onClick:Clear()
    self.btnSpecialFunc.onClick:Clear()
    CloseEvent(Config.EventType.FreashSettingDraw,    self.loadCityModeCharactor, self)
    CloseEvent(Config.EventType.FreashCityBackground, self.loadCityBackground,    self)
    -- CloseEvent(Config.EventType.FreashRoleShowDraw, self.loadCityModeCharactor, self)
    CloseEvent(Config.EventType.CityModeReset,    self.onEventResetMode,        self)
    CloseEvent(Config.EventType.BottomChangePage, self.onEventBottomChangePage, self)

    self.funcOpenDict = nil
    self.handlers     = nil
    self.drawWrapper[1]  = nil
    self.drawWrapper[2]  = nil
    self.NormalEnum   = nil
    self.SpecialEnum  = nil
    self.CityModeEnum = nil
    if (self.roleDraw[1] ~= nil) then
        destroyImmediate(self.roleDraw[1])
        self.roleDraw[1] = nil
    end
    if (self.roleDraw[2] ~= nil) then
        destroyImmediate(self.roleDraw[2])
        self.roleDraw[2] = nil
    end
    if mTimer ~= nil then
        mTimer:Stop()
    end

    if self.coroutineText ~= nil then
        coroutine.stop(self.coroutineText)
        self.coroutineText = nil
    end
end

function M:onShow()
    -- 主城立绘人物形象
    self:loadCityModeCharactor()
    self:refreshSwitchButtonState()
    self:refreshFuncOpenStatus()
    self:playBGM()
end
function M:onHide()
    if blendTimer then
        utimer.remove(blendTimer)
        blendTimer = nil
    end
    if delayObjTimer then
        utimer.remove(delayObjTimer)
        delayObjTimer = nil
    end
    if (self.roleDraw[1] ~= nil) then
        destroyImmediate(self.roleDraw[1])
        self.roleDraw[1] = nil
    end
    if (self.roleDraw[2] ~= nil) then
        destroyImmediate(self.roleDraw[2])
        self.roleDraw[2] = nil
    end
    if mTimer ~= nil then
        mTimer:Stop()
    end
    if self.coroutineText ~= nil then
        coroutine.stop(self.coroutineText)
        self.coroutineText = nil
    end
end

--- desc: 注册上层回调事件
--- @param name   string    事件名
--- @param func   function  回调
--- @param target any       回调对象
function M:registerHandler(name, func, target)
    if not name or not func then
        return
    end

    if not self.handlers then
        --- 回调集合
        --- @type table<string, {func: function, target: any}>
        self.handlers = {}
    end

    name = tostring(name)
    if self.handlers[name] then
        return
    end
    self.handlers[name] = { func = func, target = target }
end

--- desc: 重置主城状态
--- @private
function M:onEventResetMode(mode)
    if self.ctrlModeSwitch == nil then
        return
    end
    self.ctrlModeSwitch.selectedIndex = mode or M.CityModeEnum.Normal
    if self.ctrlNormalFunc.selectedIndex == 1 then
        self.ctrlNormalFunc.selectedIndex = 2
    end
    if self.ctrlSpecialFunc.selectedIndex == 1 then
        self.ctrlSpecialFunc.selectedIndex = 2
    end
end

--- desc: 切换界面底部标签
--- @private
function M:onEventBottomChangePage(page)
    if page ~= EnumConst.BottomPageType.city then
        self:stopHeroVoice()
    end
end

--- desc: 模式切换功能是否解锁
--- @return boolean
function M:isModeSwitchUnlock()
    return logicMgr.FuncOpenManager.isOpen(Config.FuncOpenType.CityModeSwitch, false)
end

--- desc: 获取当前主城模式
--- @return number CityModeEnum
function M:getCurrMode()
    return tonumber(GetPlayerPrefs("city_mode") or M.CityModeEnum.Charactor)
end

--- desc: 存储主城模式
--- @param mode number 当前模式
function M:setCurrMode(mode)
    SetPlayerPrefs("city_mode", mode)
    AudioManager.SetCityBGM(mode)
end

--- desc: 播放背景音乐
---@private
function M:playBGM(mode)
    local mode  = mode or self:getCurrMode()
    local bgmId = mode == M.CityModeEnum.Normal and 5 or 316
    AudioManager.Play(bgmId)
end

--- 功能解锁条件管理
---@private
function M:refreshFuncOpenStatus()
    local funcOpenMgr = logicMgr.FuncOpenManager
    for key, value in pairs(self.funcOpenDict) do
        local openType, obj = value[1], value[2]
        funcOpenMgr.addStatusCtr(PanelNames.UICity, openType, "city_func" .. key, obj)
    end
end

--- desc: 刷新模式切换按钮状态
function M:refreshSwitchButtonState()
    local unlock = self:isModeSwitchUnlock()
    self.ctrlSwitchBtn.selectedIndex = not unlock and 1 or 0
end

--- desc: 加载主城背景
--- @private
--- @param inputBgId number 背景id
function M:loadCityBackground(inputBgId)
    local bgId = HeroManager.getCityBackground()
    if inputBgId then
        bgId = inputBgId
    end

    local cfg = dataTable.getDataTableByStringId("t_citybg", tostring(bgId))
    if cfg then
        self.imgBgTop.url    = cfg.f_BgImgUp
        self.imgBgBottom.url = cfg.f_BgImgDown
    else
        printError("主城背景配置文件出错 ->t_citybg: ", bgId)
    end
end

--- desc: 加载主城人物立绘形象
--- @private
--- @param inputHeroId number 角色id
function M:loadCityModeCharactor(inputHeroId)
    local index = 1
    if (self.roleDraw[3-index] == nil) then
        index = 2
    end
    local heroId = tostring(logicMgr.SkinManager.getHeroModelIdBySkinId(logicMgr.HeroManager.GetRoleInfo("pic")))
    local tempNum = 0
    if inputHeroId then
        heroId = tostring(inputHeroId)
        tempNum = 200
    end

    -- 屏蔽未开放的英雄（临时处理）
    if not logicMgr.HeroManager.isOpenHeroAtlas(heroId) then
        heroId = 19
    end

    if mTimer ~= nil then
        mTimer:Stop()
    end
    bCanClick = true

    self.heroModelId = heroId
    local useSkinId = logicMgr.SkinManager.getHeroSkinId(heroId)
    local imageName = HeroManager.GetHeroDrawingPath(heroId, nil, useSkinId)
    resMgr:LoadModelPrefab(imageName, function(go)
        print("LoadModelPrefab: ", imageName)
    end)
    resMgr:LoadModelPrefab(imageName, function(go)
        if self.drawWrapper[index] == nil then
            self.drawWrapper[index] = GoWrapper.New();
        end
        if not go then return end
        if (self.roleDraw[index] ~= nil) then
            destroyImmediate(self.roleDraw[index])
            self.roleDraw[index] = nil
        end
        drawIndex = index
        self.roleDraw[index] = newObject(go)
        self.roleDraw[index].name = imageName
        Util.PlayAction(self.roleDraw[index], "idle1", true)
        self.roleDraw[index]:AddComponent(TCanvas)
        self.drawWrapper[index]:SetWrapTarget(self.roleDraw[index], true)
        self.drawParent[index]:SetNativeObject(self.drawWrapper[index]);
        Util.Lua_SetLocalPositionXYZ(self.roleDraw[index].transform, 0, 0, 0)
        local scales, pos = logicMgr.HeroManager.GetHeroLocationResizeBySkinId(useSkinId)
        Util.Lua_SetLocalScaleXYZ(self.roleDraw[index].transform, scales[1] * 1, scales[2] * 1, 1)
        Util.Lua_SetLocalPositionXYZ(self.roleDraw[index].transform, pos[1]-tempNum, pos[2], 0)
        self.roleDraw[index]:GetComponent("SkeletonGraphic").AnimationState:SetAnimation(0, "idle1", true)
        if delayObjTimer then
            utimer.remove(delayObjTimer)
            delayObjTimer = nil
        end
        if blendTimer then
            utimer.remove(blendTimer)
            blendTimer = nil
        end
        if inputHeroId then
            if self.roleDraw[index] then
                delayObjTimer = FadeFuc(self.roleDraw[index],1,10,nil,function(temp)
                    Util.Lua_SetLocalPositionXYZ(self.roleDraw[index].transform, pos[1]+tempNum*(temp-1), pos[2],0)
                end)
            end
            if self.roleDraw[3-index] then
                blendTimer = FadeFuc(self.roleDraw[3-index],0,5,function()
                    if self.roleDraw[3-index] ~= nil then
                        destroyImmediate(self.roleDraw[3-index]);
                        self.roleDraw[3-index]= nil
                    end
                end,function(temp)
                    if self.roleDraw[3-index] then
                        local trans = self.roleDraw[3-index].transform
                        Util.Lua_SetLocalPositionXYZ(self.roleDraw[3-index].transform, trans.localPosition.x+tempNum*temp, trans.localPosition.y, trans.localPosition.z)
                    end
                end)
            end
        end
    end)
end

--- 英雄语音文字展示
--- @private
--- @param text string 文本
function M:refreshHeroText(text)
    local txtHeroSpeak = self.txtHeroSpeak
    if txtHeroSpeak == nil then return end

    if text == nil or text == "" then
        txtHeroSpeak.text = text
        return
    end

    txtHeroSpeak.alpha = 1
    txtHeroSpeak.text  = text
    if self.effTextHide ~= nil then
        self.effTextHide:Stop(true, false)
        self.effTextHide:Play(function() txtHeroSpeak.text = "" end)
    end

    -- if self.coroutineText ~= nil then
    --     coroutine.stop(self.coroutineText)
    --     self.coroutineText = nil
    -- end

    -- local tempstr = Util.StringToArr(text)
    -- local textArr = string_split(tempstr, "&")
    -- table.insert(textArr, "")

    -- local function playTextAni()
    --     local textCnt = #textArr
    --     for i = 2, textCnt do
    --         local t = i == textCnt and TEXT_DELAY or TEXT_SPEED
    --         coroutine.wait(t)

    --         txtHeroSpeak.text = txtHeroSpeak.text .. textArr[i]
    --         if i == textCnt then
    --             txtHeroSpeak.text = ""
    --         end
    --     end
    -- end
    -- txtHeroSpeak.text = textArr[1]
    -- self.coroutineText = coroutine.start(playTextAni)
end

--- desc: 播放立绘动画
--- @private
function M:playDrawAnimation()
    local model = self.roleDraw[drawIndex]
    if model == nil or not bCanClick then
        return
    end
    if mTimer ~= nil then
        mTimer:Stop()
    end

    local g = model:GetComponent("SkeletonGraphic")
    g.AnimationState:SetAnimation(0, "idle2", false)

    local function delayIdle()
        bCanClick = true
        if model:GetComponent("SkeletonGraphic").AnimationState then
            if model:GetComponent("SkeletonGraphic").AnimationState.Data.SkeletonData:FindAnimation("idle3") ~= nil then
                model:GetComponent("SkeletonGraphic").AnimationState:SetAnimation(0, "idle3", true)
            else
                model:GetComponent("SkeletonGraphic").AnimationState:SetAnimation(0, "idle1", true)
            end
        end
    end

    local idleAniTime = g.AnimationState.Data.SkeletonData:FindAnimation("idle2").Duration
    if mTimer == nil then
        mTimer = Timer.New(delayIdle, idleAniTime, 1, false)
    else
        mTimer:Reset(delayIdle, idleAniTime, 1, false)
    end
    mTimer:Start()
    bCanClick = false
end

--- desc: 播放英雄语音和文本
--- @private
function M:playVoiceAndText()
    if self.heroModelId == nil then
        return
    end

    local heroModelId = self.heroModelId
    local skinId = 0
    local clickTag1 = AudioManager.InteractionTagTable[6].Tag
    local clickTag2 = AudioManager.InteractionTagTable[7].Tag
    local clickTag3 = AudioManager.InteractionTagTable[8].Tag

    -- 随机一句话
    local index = Mathf.Random(1, 3)
    local talkLevel = 0
    local interactionTag = 0
    if index == 2 then
        interactionTag = clickTag2
        talkLevel = 1
    elseif index == 3 then
        interactionTag = clickTag3
        talkLevel = 2
    else
        interactionTag = clickTag1
        talkLevel = 0
    end

    local interactionId = heroModelId .. skinId .. interactionTag
    local interactionInfo = G.dataTable["t_interaction"][tostring(interactionId)]
    if interactionInfo then
        if not string.IsNilOrEmpty(interactionInfo.f_LineText) then
            local introStr = getLanguage(interactionInfo.f_LineText)
            if not string.IsNilOrEmpty(introStr) then
                self:refreshHeroText(introStr)
            end
        end
    end

    AudioManager.TryStopInteractionVoice()
    AudioManager.SetRTPCValue("talk_level", talkLevel)
    AudioManager.PlayInteractionVoice(heroModelId, 0, interactionTag)
end

--- desc: 停止语音播放
--- @private
function M:stopHeroVoice()
    AudioManager.TryStopInteractionVoice()
end

--- desc: 点击功能列表item
--- @private
function M:onClickNormalFuncItem(item)
    local index = self.listNormalFunc:GetChildIndex(item.data)
    self:callHandler(NORMAL_TAG + index)
end
--- desc: 点击功能列表item
--- @private
function M:onClickSpecialFuncItem(item)
    local index = self.listSpecialFunc:GetChildIndex(item.data)
    self:callHandler(SPECIAL_TAG + index)
end

--- desc: 更换主城角色立绘形象
--- @private
function M:onClickCharactorChange()
    if logicMgr.FuncOpenManager.isOpen(Config.FuncOpenType.RoleInfo, true) then
        AudioManager.Play(1024)
        -- showUI(PanelNames.UIHeadInfo)
        showUI(PanelNames.UISelectShow)
    end
end
--- desc: 切换主城模式
--- @private
function M:onClickModeSwitch()
    if not logicMgr.FuncOpenManager.isOpen(Config.FuncOpenType.CityModeSwitch, true, self.btnModeSwitch) then
        return
    end

    -- 播放音效
    AudioManager.Play(4022)

    local CityModeEnum = M.CityModeEnum
    local mode = self:getCurrMode() == CityModeEnum.Normal and CityModeEnum.Charactor or CityModeEnum.Normal
    self.ctrlModeSwitch.selectedIndex = mode
    self:setCurrMode(mode)
    self:playBGM(mode)

    if mode == CityModeEnum.Normal then
        self:stopHeroVoice()
    end
end

--- desc: 旧城区
---@private
function M:onClickNormalFunc()
    AudioManager.Play(1001)
end
--- desc: 中心区
---@private
function M:onClickSpecialFunc()
    AudioManager.Play(1001)
end

--- desc: 点击空白处收起功能栏
--- @private
function M:onClickBlank()
    if self.ctrlNormalFunc.selectedIndex == 1 then
        self.ctrlNormalFunc.selectedIndex = 2
    end
    if self.ctrlSpecialFunc.selectedIndex == 1 then
        self.ctrlSpecialFunc.selectedIndex = 2
    end
    self:playDrawAnimation()
    self:playVoiceAndText()
end

--- desc: 调用上层回调事件
--- @private
--- @param name string 事件名
function M:callHandler(name)
    if not name then
        return
    end

    if not self.handlers then
        return
    end

    name = tostring(name)
    local handler = self.handlers[name]
    if not handler then
        return
    end

    -- 功能拦截
    local funcOpenData = self.funcOpenDict[name]
    if funcOpenData and not logicMgr.FuncOpenManager.isOpen(funcOpenData[1], true, funcOpenData[2]) then
        return
    end

    local func = handler.func
    if func then
        func()
    end
end

return M